home *** CD-ROM | disk | FTP | other *** search
/ SPACE 2 / SPACE - Library 2 - Volume 1.iso / magazi~1 / 278 / dialogs.3 next >
Text File  |  1986-11-01  |  21KB  |  476 lines

  1.     Permission to reprint or excerpt is granted only if the following line
  2.     appears at the top of the article:
  3.  
  4.       ANTIC PUBLISHING INC., COPYRIGHT 1985. REPRINTED BY PERMISSION.
  5.  
  6.  
  7.  
  8.     Professional GEM  by Tim Oren
  9.     Column #3 - The Dialog Handler
  10.  
  11.  
  12.     A MEANINGFUL DIALOG
  13.  
  14.        This  issue  of ST PRO GEM begins an exploration of ST GEM's dialog
  15.     handler.  I will discuss basic system calls for presenting the dialog,
  16.     and  then continue with techniques for initializing and reading on/off
  17.     button  and  "radio"  button  objects.   We  will also take some short
  18.     side-trips  into the operation of the GEM Resource Construction Set to
  19.     assist you in building these dialogs.
  20.  
  21.        There are a number of short C routines which accompany this column.
  22.     These  are  stored  as  file  GEMCL3.XMO in DL 5 on SIG*ATARI.  Before
  23.     reading  this  column,  you  should  visit  SIG*ATARI (go pcs-132) and
  24.     download this file.
  25.  
  26.  
  27.     DEFINING TERMS
  28.  
  29.        A  dialog  box is an "interactive form" in which the user may enter
  30.     text  and  indicate selections by pointing with the mouse.  Dialogs in
  31.     GEM  are  "modal",  that  is,  when a dialog is activated other screen
  32.     functions  such  as  menus and window controls are suspended until the
  33.     dialog is completed.
  34.  
  35.        In  most  cases,  the visual structure of a GEM dialog is specified
  36.     within   your   application's   resource   file.    The  GEM  Resource
  37.     Construction Set (RCS) is used to build a picture of the dialog.
  38.  
  39.        When the RCS writes out a resource, it converts that picture into a
  40.     tree  of GEM drawing objects and stores this data structure within the
  41.     resource.   Before  your  application  can display the dialog, it must
  42.     load this resource file and find the address of the tree which defines
  43.     the dialog.
  44.  
  45.        To  load  a  resource, the AES checks its size and allocates memory
  46.     for  the  load.   It  then  reads  in the resource, adjusting internal
  47.     pointers  to  reflect  the  load  address.   Finally, the object sizes
  48.     stored  in  the resource are converted from characters to pixels using
  49.     the system font size.
  50.  
  51.        (A  note for those with Macintosh experience:  Although Mac and GEM
  52.     resources share a name, there are fundamental differences which can be
  53.     misleading.  A Mac resource is a fork within a file; a GEM resource is
  54.     a  TOS  file  by  itself.   Mac  resources  may be paged in and out of
  55.     memory;  GEM  resources  are monolithic.  GEM resources are internally
  56.     tree  structured;  Mac  resources  are  not.   Finally,  Mac resources
  57.     include  font information, while ST GEM does this with font loading at
  58.     the VDI level.)
  59.  
  60.        The resource load is done with the GEM AES call:
  61.  
  62.         ok = rsrc_load(ADDR("MYAPP.RSC"));
  63.  
  64.        "MYAPP"  should  be  replaced  with  the  name  of  your  program.
  65.     Resources   conventionally   have  the  same  primary  name  as  their
  66.     application,  with  the  RSC  extent name instead of PRG.  The ok flag
  67.     returned  by rsrc_load will be FALSE is anything went wrong during the
  68.     load.
  69.  
  70.        The most common causes of failure are the resource not being in the
  71.     application's  subdirectory,  or  lack of sufficient memory for GEM to
  72.     allocate  space for the resource.  If this happens, you must terminate
  73.     the program immediately.
  74.  
  75.        Once  you  have  loaded  the  resource,  you  find the address of a
  76.     dialog's object tree with:
  77.  
  78.         rsrc_gaddr(R_TREE,MYDIALOG,&tree);
  79.  
  80.     Tree  is  a 32-bit variable which will receive the address of the root
  81.     node of the tree.
  82.  
  83.        The  mnemonic  MYDIALOG  should  be replaced with the name you gave
  84.     your  dialog  when  defining  it in the RCS.  At the same time that it
  85.     writes  the resource, RCS generates a corresponding .H file containing
  86.     tree  and  object  names.  In order to use these mnemonics within your
  87.     program,  you  must  include  the name file in your compile:  #include
  88.     "MYAPP.H"
  89.  
  90.  
  91.     BUG ALERT!
  92.  
  93.        When  using  the  DRI/Alcyon  C  compiler,  .H files must be in the
  94.     compiler's  home  directory  or  they  will  not  be  found.   This is
  95.     especially  annoying  using a two floppy drive ST development system.
  96.     The  only way around this is to explicitly reference an alternate disk
  97.     in the #include, for instance:  "B:MYAPP.H"
  98.  
  99.        Now  that  the  address  of the dialog tree has been found, you are
  100.     ready to display it.  The standard (and minimal) sequence for doing so
  101.     is  given  in  routine  hndl_dial() in the download.  We will now walk
  102.     through each step in this procedure.
  103.  
  104.        The  form_center call establishes the location of the dialog on the
  105.     screen.   Dialog  trees  generated by the RCS have an undefined origin
  106.     (upper-left corner).
  107.  
  108.        Form_center  computes  the  upper-left location necessary to center
  109.     the dialog on the screen, and inserts it into the OB_X and OB_Y fields
  110.     of the ROOT object of the tree.  It also computes the screen rectangle
  111.     which   the  dialog  will  occupy  on  screen  and  writes  its  pixel
  112.     coordinates into variables xdial, ydial, wdial, and hdial.
  113.  
  114.        There  is  one peculiarity of form_center which occasionally causes
  115.     trouble.   Normally  the rectangle returned in xdial, etc., is exactly
  116.     the same size as the basic dialog box.
  117.  
  118.        However,  when  the OUTLINED enhancement has been specified for the
  119.     box,  form_center adds a three pixel margin to the rectangle returned.
  120.     This  causes the screen area under the outline to be correctly redrawn
  121.     later  (see below).  Note that OUTLINED is part of the standard dialog
  122.     box  in  the  RCS.   Other enhancements, such as SHADOWED or "outside"
  123.     borders  are  NOT handled in this fashion, and you must compensate for
  124.     them in your code.
  125.  
  126.        The  next  part  of  the  sequence  is a form_dial call with a zero
  127.     parameter.   This  reserves  the screen for the dialog action about to
  128.     occur.   Note  that  the  C  binding  given  for  form_dial in the DRI
  129.     documents is in error: there are nine parameters, not five.  The first
  130.     set  of  xywh  arguments is actually used with form_dial calls 1 and 2
  131.     only, but place holders must be supplied in all cases.
  132.  
  133.        The succeeding form_dial call (parameter one) animates a "zoom box"
  134.     on  the  screen  which moves and grows from the first screen rectangle
  135.     given to the second rectangle, where the dialog will be displayed.
  136.  
  137.        The  use of this call is entirely optional.  In choosing whether to
  138.     use it or not, you should consider whether the origin of the "zoom" is
  139.     relevant  to the operation.  For instance, a zoom from the menu bar is
  140.     relatively meaningless, while a zoom from an object about to be edited
  141.     in  the  dialog  provides visual feedback to the user, showing whether
  142.     the correct object was chosen.
  143.  
  144.        If the origin is not relevant, then the zoom is just a time-waster.
  145.     If  you  decide  to  include  these  effects, consider a "preferences"
  146.     option  in your app which will allow the experienced and jaded user to
  147.     turn them off in the interests of speed.
  148.  
  149.        The  objc_draw  call  actually  displays the dialog on the screen.
  150.     Note  that  the address of the tree, the beginning drawing object, and
  151.     the  drawing  depth  are passed as arguments, as well as the rectangle
  152.     allotted for the dialog.
  153.  
  154.        In  general,  dialogs  (and  parts  of  dialogs)  are  ALWAYS drawn
  155.     beginning  at  the  ROOT  (object zero).  When you want to draw only a
  156.     portion  of  the  dialog,  adjust  the clipping rectangle, but not the
  157.     object  number.   This  ensures  that  the background of the dialog is
  158.     always drawn correctly.
  159.  
  160.        The  objc_xywh()  utility  in  the download can be used to find the
  161.     clipping rectangle for any object within a dialog, though you may have
  162.     to  allow  an  extra  margin  is  you  have used shadows, outlines, or
  163.     outside borders with the object.
  164.  
  165.        Calling  form_do  transfers  control to the AES, which animates the
  166.     dialog for user interaction.  The address of the dialog tree is passed
  167.     as  a  parameter.   The  second paramter is the number of the editable
  168.     object at which the text cursor will first be positioned.  If you have
  169.     no text fields, pass a zero.  Note that again the DRI documents are in
  170.     error:  passing  a  -1  default may crash the system.  Also be careful
  171.     that  the default which you specify is actually a text field; no error
  172.     checking is performed.
  173.  
  174.        The  form_do  call  returns  the  number of the object on which the
  175.     clicked to terminate the dialog.  Usually this is a button type object
  176.     with  the  EXIT  and  SELECTABLE  attributes set.  Setting the DEFAULT
  177.     attribute  as  well  will  cause  an exit on that object is a carriage
  178.     return is struck while in the dialog.
  179.  
  180.        If  the  top  bit  of the return is set, it indicates that the exit
  181.     object   had   the   TOUCHEXIT  attribute  and  was  selected  with  a
  182.     double-click.  Since very few dialogs use this combination, the sample
  183.     code simply masks off the top bit.
  184.  
  185.        The next form_dial call reverses the "zoom box", moving it from the
  186.     dialog's  location back to the given x,y,w,h.  The same cautions apply
  187.     here as above.
  188.  
  189.        The final form_dial call tells GEM that the dialog is complete, and
  190.     that  the screen area occupied by the dialog is now considered "dirty"
  191.     and  needs  to  be  redrawn.   Using the methods described in our last
  192.     column, GEM then sends redraws to all windows which were overlaid, and
  193.     does any necessary redrawing of the menu or desktop itself.
  194.  
  195.        There  is one notable "feature" of form_dial(3):  It always redraws
  196.     an  area which is two pixels wider and higher than your request!  This
  197.     was  probably included to make sure that drop-shadows were cleaned up,
  198.     and is usually innocuous.
  199.  
  200.  
  201.     A HANDY TRICK
  202.  
  203.        Use  of  the  form_dial(3) call is not limited to dialogs.  You can
  204.     use  it  to  force  the  system to redraw any part of the screen.  The
  205.     advantage of this method is that the redraw area need not lie entirely
  206.     within a window, as was necessary with the send_redraw method detailed
  207.     in  the  last  column.  A disadvantage is that this method is somewhat
  208.     slower, since the AES has to decide who gets the redraws.
  209.  
  210.  
  211.     CLEAN UP
  212.  
  213.        As  a  last step, you need to clear the SELECTED flag in the object
  214.     which  was  clicked.   If you do not do this, the object will be drawn
  215.     inverted  the next time you call the dialog.  You could clear the flag
  216.     with  the GEM objc_change call, but it is inefficient since you do not
  217.     need to redraw the object.
  218.  
  219.        Instead,  use  the desel_obj() code in the download, which modifies
  220.     the  object's OB_STATE field directly.  Assuming that ret_obj contains
  221.     the exit object returned by hndl_dial, the call:
  222.  
  223.         desel_obj(tree, ret_obj);
  224.  
  225.     will do the trick.
  226.  
  227.  
  228.     RECAP
  229.  
  230.        The  basic  dialog  handling method I have described contains three
  231.     steps:  initialization  (rsrc_gaddr), dialog presentation (hndl_dial),
  232.     and cleanup (desel_obj).
  233.  
  234.        As  we  build more advanced dialogs, these same basic steps will be
  235.     performed,  but  they will grow more complex.  The initialization will
  236.     include  setting  up  proper  object  text and states, and the cleanup
  237.     phase  will  also  interrogate the final states of objects to find out
  238.     what the user did.
  239.  
  240.  
  241.     BUTTON, BUTTON
  242.  
  243.        The  simple  dialogs  described  above contain only exit buttons as
  244.     active  objects.   As  such, they are little more than glorified alert
  245.     boxes.
  246.  
  247.        We  will  now  increase  the  complexity  a  little  by considering
  248.     non-exit  buttons.   These  are  constructed by setting the SELECTABLE
  249.     attribute on a button object.
  250.  
  251.        At  run-time, such an object will toggle its state between selected
  252.     (highlighted)  and  non-selected whenever the user clicks on it.  (You
  253.     can  set  the  SELECTABLE  attribute of other types of objects and use
  254.     them instead of actual buttons, but be sure that the user will be able
  255.     to figure out what you intend!)
  256.  
  257.        Having  non-exit  buttons  forces  us  to  consider  the problem of
  258.     initializing  them  before the dialog, and interrogating and resetting
  259.     them afterward.
  260.  
  261.        Since  a  button  is a toggle, it is usually associated with a flag
  262.     variable  in  the  program.  As part of the initialization, you should
  263.     test the flag variable, and if true call:
  264.  
  265.         sel_obj(tree, BTNOBJ);
  266.  
  267.     which  will  cause the button to appear highlighted when the dialog is
  268.     first  drawn.   Sel_obj() is in the download.  BTNOBJ is replaced with
  269.     the  name  you gave your button when you defined it in the RCS.  Since
  270.     the  button  starts  out  deselected, you don't have to do anything if
  271.     your flag variable is false.
  272.  
  273.        After  the  dialog  has  completed,  you need to check the object's
  274.     state.   The  selectp() utility does so by masking the OB_STATE field.
  275.     You  can  simply assign the result of this test to your flag variable,
  276.     but  be  sure that the dialog was exited with an OK button, not with a
  277.     CANCEL!  Again,  remember  to  clean  up the button with desel_obj().
  278.     (It's  often easiest to deselect all buttons just before you leave the
  279.     dialog routine, regardless of the final dialog state.)
  280.  
  281.  
  282.     WHO'S GOT THE BUTTON?
  283.  
  284.        Another common use of buttons in a dialog is to select one of a set
  285.     of  possible  options.  In GEM, such objects are called radio buttons.
  286.     This  term recalls automobile radio tuners where pushing in one button
  287.     pops  out  any others.  In like fashion, selecting any one of a set of
  288.     radio buttons automatically deselects all of the others.
  289.  
  290.        To use the radio button feature, you must do some careful work with
  291.     the Resource Construction Set.
  292.  
  293.        First,  each  member  of a set of radio buttons must be children of
  294.     the  same  parent  object  within  the  object  tree.   To create this
  295.     structure,  put  a  hollow  box type object in the dialog, make it big
  296.     enough  to  hold all of the buttons, and then put the buttons into the
  297.     box one at a time.
  298.  
  299.        By  nesting the buttons within the box object, you force them to be
  300.     its  children.   Each of the buttons must have both the SELECTABLE and
  301.     RADIO  BUTTON  attributes  set.   When  you are done, you may make the
  302.     containing  box  invisible  by  setting its border to zero, but do not
  303.     FLATTEN it!
  304.  
  305.        Since  each  radio  button  represents a different option, you must
  306.     usually  assign  a name to each object.  When initializing the dialog,
  307.     you  must  check  which  option  is  currently  set,  and  turn on the
  308.     corresponding button only.  A chain of if-then-else structures assures
  309.     that only one button will be selected.
  310.  
  311.        At  the  conclusion  of the dialog, you must check each button with
  312.     selectp()  and make the appropriate adjustments to internal variables.
  313.     Again,  an if-then-else chain is appropriate since only one button may
  314.     be  selected.   Either deselect the chosen button within this chain or
  315.     do them all at the end.
  316.  
  317.        There is one common use of radio buttons in which you may short-cut
  318.     this procedure.  If the buttons each represent one possible value of a
  319.     numeric variable, for instance, a set of selector buttons representing
  320.     colors  from  zero  to  seven, then you can compute the initial object
  321.     directly.
  322.  
  323.        In  order  for  this  technique  to  work,  you  must use a special
  324.     capability  of  the  RCS.   Insert  the object corresponding to a zero
  325.     value  at  the  top  (or  left) of your array of buttons, then put the
  326.     "one" button below (or right) of it, and so on.
  327.  
  328.        When  the  buttons  are  complete,  the  SORT  operation is used to
  329.     guarantee  that  the top/left object is in fact the first child of the
  330.     parent  box with the others following in order.  Due to the details of
  331.     object  tree structure (to be discussed in the next column), this will
  332.     guarantee that these objects are contiguous in the resource.
  333.  
  334.        If  you  assign  a name (say BUTTON1) to the first button, then you
  335.     can initialize the correct button with the call:
  336.  
  337.         sel_obj(tree, BUTTON1 + field);
  338.  
  339.     where field is the variable of interest.
  340.  
  341.        When  the  dialog  is  complete,  you can scan the radio buttons to
  342.     compute  the  new  value  for  the  underlying variable.  The encode()
  343.     procedure  in  the  download  will  do  this.   As always, remember to
  344.     deselect the buttons at the end.
  345.  
  346.        You  can use offsets or multipliers if your variable's values don't
  347.     start  with zero or increment by one.  If the values are irregular you
  348.     may be able to use a lookup table, at the cost of additional code.
  349.  
  350.  
  351.     COMING UP NEXT
  352.  
  353.        In the next column, I will discuss the internal structure of object
  354.     trees.   Then  we'll use that knowledge to build a piece of code which
  355.     will "walk" an entire tree and apply a function to each object.  We'll
  356.     apply  this code to do all of the button deselects with a single call!
  357.     I'll  also look at handling editable text fields and discuss some ways
  358.     to alter a dialog's appearance at run-time.
  359.  
  360.  
  361.     DISPELL GREMLINS
  362.  
  363.        An  editing error caused an omission in the first installment of ST
  364.     PRO  GEM.   The window components RTARROW and DNARROW should have been
  365.     listed along with HSLIDE as the horizontal equivalents of the vertical
  366.     slider components which were discussed.
  367.  
  368.  
  369.  
  370.  
  371. >>>>>>>>>>>>>>>>>>>>>>> Basic Dialog Handler <<<<<<<<<<<<<<<<<<<<<<<
  372.  
  373.      WORD
  374. hndl_dial(tree, def, x, y, w, h)
  375.      LONG     tree;
  376.      WORD     def;
  377.      WORD     x, y, w, h;
  378.      {
  379.      WORD     xdial, ydial, wdial, hdial, exitobj;
  380.  
  381.      form_center(tree, &xdial, &ydial, &wdial, &hdial);
  382.      form_dial(0, x, y, w, h, xdial, ydial, wdial, hdial);
  383.      form_dial(1, x, y, w, h, xdial, ydial, wdial, hdial);
  384.      objc_draw(tree, ROOT, MAX_DEPTH, xdial, ydial, wdial, hdial);
  385.      exitobj = form_do(tree, def) & 0x7FFF;
  386.      form_dial(2, x, y, w, h, xdial, ydial, wdial, hdial);
  387.      form_dial(3, x, y, w, h, xdial, ydial, wdial, hdial);
  388.      return (exitobj);
  389.      }
  390.  
  391.  
  392. >>>>>>>>>>>>>>>>>>>>>>> Object rectangle utility <<<<<<<<<<<<<<<<<<<<<<<<<
  393.  
  394.      VOID
  395. objc_xywh(tree, obj, p)          /* get x,y,w,h for specified object     */
  396.      LONG     tree;
  397.      WORD     obj;
  398.      GRECT     *p;
  399.      {
  400.      objc_offset(tree, obj, &p->g_x, &p->g_y);
  401.      p->g_w = LWGET(OB_WIDTH(obj));
  402.      p->g_h = LWGET(OB_HEIGHT(obj));
  403.      }
  404.  
  405.  
  406. >>>>>>>>>>>>>>>>>>>>>> Sample radio buttons after dialog <<<<<<<<<<<<<<<<<<<<
  407.  
  408.      WORD
  409. encode(tree, ob1st, num)
  410.      LONG     tree;
  411.      WORD     ob1st, num;
  412.      {
  413.      for (; num--; )
  414.           if (selectp(ob1st+num))
  415.                return(num);
  416.      return (-1);
  417.      }
  418.  
  419.  
  420. >>>>>>>>>>>>>>>>>>>>>>> Object flag utilities <<<<<<<<<<<<<<<<<<<<<<<<<<<
  421.  
  422.      VOID
  423. undo_obj(tree, which, bit)     /* clear specified bit in object state     */
  424.      LONG     tree;
  425.      WORD     which, bit;
  426.      {
  427.      WORD     state;
  428.  
  429.      state = LWGET(OB_STATE(which));
  430.      LWSET(OB_STATE(which), state & ~bit);
  431.      }
  432.  
  433.      VOID
  434. desel_obj(tree, which)          /* turn off selected bit of spcfd object*/
  435.      LONG     tree;
  436.      WORD     which;
  437.      {
  438.      undo_obj(tree, which, SELECTED);
  439.      }
  440.  
  441.      VOID
  442. do_obj(tree, which, bit)     /* set specified bit in object state     */
  443.      LONG     tree;
  444.      WORD     which, bit;
  445.      {
  446.      WORD     state;
  447.  
  448.      state = LWGET(OB_STATE(which));
  449.      LWSET(OB_STATE(which), state | bit);
  450.      }
  451.  
  452.      VOID
  453. sel_obj(tree, which)          /* turn on selected bit of spcfd object     */
  454.      LONG     tree;
  455.      WORD     which;
  456.      {
  457.      do_obj(tree, which, SELECTED);
  458.      }
  459.  
  460.      BOOLEAN
  461. statep(tree, which, bit)
  462.      LONG     tree;
  463.      WORD     which;
  464.      WORD     bit;
  465.      {
  466.      return ( (LWGET(OB_STATE(which)) & bit) != 0);
  467.      }
  468.  
  469.      BOOLEAN
  470. selectp(tree, which)
  471.      LONG     tree;
  472.      WORD     which;
  473.      {
  474.      return statep(tree, which, SELECTED);
  475.      }
  476.